//#ifndef GRAPH_H
//#define GRAPH_H
#ifndef GM3GRAPH_H
#define GM3GRAPH_H


#include "cvec.h"

#include <string>
#include <vector>


//namespace GM3{

class KDTree;

class GM3Graph
{
    friend class KDTree;

    public:
        struct node{
            cvec p;
            std::string name;
            float weight;
            int parent;
            unsigned int UID;
            node(cvec ip=cvec(), std::string iname=std::string(),
                    float iweight=1.f, int iparent=-1, unsigned int iID=0){
                p=ip; name=iname; weight=iweight; parent=iparent; UID=iID;
            }
        };
        struct edge{
            unsigned int id1, id2;
            float weight;
            int sampList;
            edge(unsigned int iid1=0, unsigned int iid2=0,
                    float iweight=1.f, int iSL=-1){
                id1=iid1; id2=iid2; weight=iweight; sampList = iSL;
            }
        };

        inline static bool edgeEqual(edge e1, edge e2){
            return e1.id1==e2.id1 && e1.id2==e2.id2;
        }

        GM3Graph(const char *fn=0);
        GM3Graph(std::vector<node> n, std::vector<edge> e);
        ~GM3Graph();


        //graph access
        inline std::vector<node> &nodesRef(){ return nodes; }
        inline std::vector<edge> &edgesRef(){ return edges; }

        inline unsigned int numNodes(){ return (unsigned int)nodes.size(); }
        inline unsigned int numEdges(){ return (unsigned int)edges.size(); }

        inline void nodeAt(int i, node n){ nodes[i] = n; }
        inline node nodeAt(int i){ return nodes.at(i); }
        inline edge edgeAt(int i){ return edges.at(i); }

        inline void getPos(int i, float *p){
            p[0] = nodes[i].p.x();
            p[1] = nodes[i].p.y();
        }
        inline void setPos(int i, float *p){ 
            nodes[i].p.x(p[0]);
            nodes[i].p.y(p[1]);
        }

        void getNodePositions(float *);
        void setNodePositions(float *);
        void getNodeWeights(float *);
        void getEdgeWeights(float *);


        //FIXME: should these be public?
        //node and edge vectors--store associated information
        std::vector<node> nodes;
        std::vector<edge> edges;



        //graph setup
        void loadFromFile(const char *fn);
        void loadFromPassed(std::vector<node> n, std::vector<edge> e);
        void randomize();
        void sortEdges();

        //rescale the graph to [0,1]
        void rescale();
        void boundingBox(float &x0, float &y0, float &x1, float &y1);

        //Multilevel stuff
        GM3Graph *maxIndptSet();
        void setLayoutFromMIS(GM3Graph *g, float j);

        //Multipole stuff
        std::vector<unsigned int> buildKDTree();
        std::vector<unsigned int> buildKDTree(unsigned int thresh);

        inline KDTree *tree(){ return kd; }

        //handle node and edge renumbering
        void translateEdges(std::vector<unsigned int> &);
        void translateParents(std::vector<unsigned int> &);

    private:
        static const unsigned int buffsize = 1024; //max MatrixMarket line

        static float jitter; //random displacement of nodes in setLayoutfromMIS
        KDTree *kd;

        //general GM3Graph loading
        void skip(std::ifstream &in, char c);
        void createBlankNodes(unsigned int);
        void formatLoadedData();
        void fileCleanup(std::ifstream &f);

        //custom node/link loading
        // Returns 0 on failure, non-zero on success
        int loadNodEdg(std::string fn);
        int loadNodeFile(std::string fn);
        int loadEdgeFile(std::string fn);

        //matrix market loading
        // Returns 0 on failure, non-zero on success
        int loadMTX(std::string fn);
        int processMTXHeader(std::ifstream &);
        int processMTXComments(std::ifstream &);
        int processMTXStats(std::ifstream &, unsigned int &N, unsigned int &E);
        int processMTXPatternLines(std::ifstream &,
                unsigned int N, unsigned int E);
        int processMTXValuedLines(std::ifstream &,
                unsigned int N, unsigned int E);

        inline void addNode(node n){ nodes.push_back(n); }
        inline void addEdge(edge e){ edges.push_back(e); }
};

//}

#endif
